﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using System.Text;
using System.IO;

namespace MetaCollection.Framework.Model
{
	/// <summary>
	/// A specification of the model for a given item.
	/// Each item in the system has the basic system level model as well as extra model properties
	/// defined through the use of an item specification.  This specification
	/// </summary>
	public interface IItemSpecification
	{
		/// <summary>
		/// Unique Identifier of this Item
		/// </summary>
		Guid Id { get; set; }
		/// <summary>
		/// Name of this item model
		/// </summary>
		/// <example>Inventory Item</example>
		string Name { get; set; }
		/// <summary>
		/// Description of what items of this model type are used for
		/// </summary>
		/// <example>A product in our Seattle Warehouse Inventory</example>
		string Description { get; set; }
		/// <summary>
		/// <c>True</c> if this model allows unknown (not specified) properties
		/// </summary>
		/// <remarks>If this value is set to false then when validation occurs of input
		/// against this item any extended properties submitted which are not in this specification
		/// will cause an exception to be thrown.</remarks>
		bool AllowUnknown { get; set; }
		/// <summary>
		/// A collection of property model rules that make up this specification
		/// </summary>
		IEnumerable<IModelProperty> Properties { get; set; }
	}

	public struct ItemSpecification : IItemSpecification
	{
		/// <summary>
		/// Unique Identifier of this Item
		/// </summary>
		[XmlAttribute()]
		public Guid Id { get; set; }
		/// <summary>
		/// Name of this item model
		/// </summary>
		/// <example>Inventory Item</example>
		[XmlAttribute()]
		public string Name { get; set; }
		/// <summary>
		/// Description of what items of this model type are used for
		/// </summary>
		/// <example>A product in our Seattle Warehouse Inventory</example>
		[XmlAttribute()]
		public string Description { get; set; }
		/// <summary>
		/// <c>True</c> if this model allows unknown (not specified) properties
		/// </summary>
		/// <remarks>If this value is set to false then when validation occurs of input
		/// against this item any extended properties submitted which are not in this specification
		/// will cause an exception to be thrown.</remarks>
		[XmlAttribute()]
		public bool AllowUnknown { get; set; }

		/// <summary>
		/// Gets the property set as an array of ModelProperty structs.
		/// </summary>
		/// <value>The property set.</value>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification="This property must exist as defined for XML serialization")]
		[XmlArray("Properties")]
		[XmlArrayItem("Property", typeof(ModelProperty))]
		public ModelProperty[] PropertySet { get { return m_Properties; } set { m_Properties = value; } }

		[XmlIgnore]
		[NonSerialized]
		ModelProperty[] m_Properties;

		/// <summary>
		/// A collection of property model rules that make up this specification
		/// </summary>
		/// <value>The properties.</value>
		[XmlIgnore]
		public IEnumerable<IModelProperty> Properties
		{
			get
			{
				if (PropertySet == null)
					return new List<IModelProperty>().AsEnumerable();
				else
					return PropertySet.Cast<IModelProperty>();
			}
			set { PropertySet = value.ToStruct().ToArray(); }
		}
	}

	public static partial class Extensions
	{
		/// <summary>
		/// Converts an enumerable set of IItemSpecification to a simple structure implementation of the interface
		/// </summary>
		/// <param name="itemSpecifications"></param>
		/// <returns>An enumerable collection of Item Instance structures.</returns>
		public static IEnumerable<IItemSpecification> ToStruct(this IEnumerable<IItemSpecification> itemSpecifications)
		{
			foreach (var spec in itemSpecifications)
			{
				yield return spec.ToStruct();
			}
			yield break;
		}

		/// <summary>
		/// Converts an IItemSpecification to a simple structure that implements the interface.
		/// </summary>
		/// <param name="spec">an IItemSpecification to convert into the ItemSpecification struct</param>
		/// <returns>a struct that implements IItemSpecification</returns>
		public static ItemSpecification ToStruct(this IItemSpecification spec)
		{
			// if the item passed in is already an ItemSpecification (struct) then return.
			if (spec is ItemSpecification)
				return (ItemSpecification)spec;

			ItemSpecification itemSpec = new ItemSpecification();
			if (spec == null)
				return itemSpec;
			itemSpec.AllowUnknown = spec.AllowUnknown;
			itemSpec.Description = spec.Description;
			itemSpec.Id = spec.Id;
			itemSpec.Name = spec.Name;
			itemSpec.Properties = spec.Properties;
			return itemSpec;
		}
	}
}
